﻿using DocumentFormat.OpenXml.Lite.Core.Office;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IO = System.IO;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;
using DocumentFormat.OpenXml.Lite.WordprocessingML.Word.Style;
using DocumentFormat.OpenXml.Lite.Core.DocumentFS;
using DocumentFormat.OpenXml.Lite.Shared;
using DocumentFormat.OpenXml.Lite.Core.Interfaces;

namespace DocumentFormat.OpenXml.Lite.WordprocessingML.Word.Base
{
    [XmlRoot("document", Namespace = Constants.WordMain)]
    public class Document : ISerializable
    {
        [XmlElement("body", Namespace = Constants.WordMain)]
        public Body Body 
        { 
            get { return body; }
            set
            {
                body = value;
                if (manager != null)
                {
                    body.DocumentId = manager.Id;
                }
            }
        }

        [XmlIgnore]
        public Styles Styles
        {
            get
            {
                return (Styles)DocumentFS.GetSandbox(manager.Id)
                    .GetDirectory("word").GetFile("style.xml").Object;
            }
        }

        [XmlNamespaceDeclarations]
        public XmlSerializerNamespaces Namespaces { get; set; }

        [XmlIgnore]
        private Body body;

        [XmlIgnore]
        private WordDocument manager;

        [XmlIgnore]
        public AppProperties AppProperties
        {
            get { return manager.AppProperties; }
            set { manager.AppProperties = value; }
        }

        [XmlIgnore]
        public CoreProperties CoreProperties
        {
            get { return manager.CoreProperties; }
            set { manager.CoreProperties = value; }
        }

        [XmlIgnore]
        public Guid Id { get { return manager.Id; } }

        private Document()
        {
            Namespaces = new XmlSerializerNamespaces();
            Namespaces.Add("w", Constants.WordMain);
            //ns.Add("xml", Constants.Xml);
        }

        internal Document(WordDocument parent)
            : this()
        {
            this.manager = parent;
        }

        #region Public Methods

        public Body CreateBody()
        {
            if (body == null)
            {
                body = new Body();
                body.DocumentId = manager.Id;
            }
            return body;
        }

        public void AddNamespace(string prefix, string ns)
        {
            if (!Namespaces.ToArray().Any(n => n.Name == prefix && n.Namespace == ns))
            {
                Namespaces.Add(prefix, ns);
            }
        }

        public Styles CreateStyle()
        {
            return manager.CreateStyle();
        }

        public string CreateChartElement(IDrawingElement element)
        {
            AddNamespace("a", Constants.Graphic);
            AddNamespace("wp", Constants.Drawing);
            AddNamespace("r", Constants.Relationship);
            return manager.CreateChartElement(element);
        }

        public string AddPicture(Image image)
        {
            return manager.CreatePictureElementFor(this, image);
        }

        public string AddPictureFor(ISerializable obj, Image image)
        {
            return manager.CreatePictureElementFor(obj, image);
        }

        public Footer CreateFooter()
        {
            return manager.CreateFooter();
        }

        public Footer Footer
        {
            get
            {
                return (Footer)DocumentFS.GetSandbox(manager.Id)
                    .GetDirectory("word").GetFile("footer.xml").Object;
            }
        }

        public Header CreateHeader()
        {
            return manager.CreateHeader();
        }

        public Header Header
        {
            get
            {
                return (Header)DocumentFS.GetSandbox(manager.Id)
                    .GetDirectory("word").GetFile("header.xml").Object;
            }
        }

        public T FindElement<T>(string relationshipId)
        {
            return manager.FindElementFor<T>(this, relationshipId);
        }

        public void Save(string path)
        {
            DocumentManager.Instance.SaveDocument(manager.Id, path);
        }

        public void Save(System.IO.Stream stream)
        {
            DocumentManager.Instance.SaveDocument(manager.Id, stream);
        }

        #endregion

        #region ISerializable members

        public byte[] Serialize()
        {
            XmlSerializer serializer = new XmlSerializer(this.GetType());
            using (var stream = new IO.MemoryStream())
            {
                using (var writer = new IO.StreamWriter(stream, Encoding.UTF8))
                {
                    serializer.Serialize(writer, this, Namespaces);
                    return stream.ToArray();
                }
            }
        }

        public void Deserialize(System.IO.Stream content)
        {
            using (var reader = XmlReader.Create(content))
            {
                reader.ReadToDescendant("document", Constants.WordMain);
                XmlSerializer serializer = new XmlSerializer(typeof(Document));
                var doc = serializer.Deserialize(reader) as Document;

                this.Body = doc.Body;
                this.Namespaces = doc.Namespaces;
            }
        }

        #endregion
    }
}
